﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace NougakudoLauncher
{
    class Startup
    {
        Process rubyProcess;

        StreamWriter rubyWriter;
        StreamReader rubyReader;

        FileSystemWatcher signalWatcher;



        public bool Run(string ruby, string args, string environment)
        {
            ProcessStartInfo procInfo = new ProcessStartInfo(ruby);
            procInfo.Arguments = args;

            // BUNDLE_GEMFILE environment parameter
            if (environment != "")
            {
                var bundle = environment.Split('=');
                procInfo.EnvironmentVariables[bundle[0]] = bundle[1];
            }

            procInfo.UseShellExecute = false;
            procInfo.RedirectStandardOutput = true;

            procInfo.RedirectStandardInput = true;

            rubyProcess = new Process();
            rubyProcess.StartInfo = procInfo;

            rubyProcess.OutputDataReceived += new DataReceivedEventHandler(rubyProcess_OutputDataReceived);
            SetSignalWatcher();
            Console.WriteLine("Startup:Ruby Start");
            rubyProcess.Start();
            rubyProcess.BeginOutputReadLine();
            //rubyReader = rubyProcess.StandardOutput;
            Logging.Write("Startup:Nougakudo ruby process({1}) start by PID={0}.", new object[] { GetProcessId(), rubyProcess.Id }, Logging.LogType.INFO);
            //rubyWriter = rubyProcess.StandardInput;

            Console.WriteLine("Ruby process wait");

            rubyProcess.WaitForExit();

            int exitCode = rubyProcess.ExitCode;    // Add ErrorCode

            Console.WriteLine("Ruby process exit");
            rubyProcess.Close();

            if (exitCode == 0)      // Add ruby start error.
                return true;        // Normal end
            else
                return false;       // Error
        }

        string GetProcessId()
        {
            return Process.GetCurrentProcess().Id.ToString();
        }

        void SetSignalWatcher()
        {
            signalWatcher = new System.IO.FileSystemWatcher(".", GetProcessId() + ".*");
            signalWatcher.Created += new System.IO.FileSystemEventHandler(signalWatcher_Created);
            signalWatcher.EnableRaisingEvents = true;
            Logging.Write("Startup:Start to observe a terminate request.", Logging.LogType.INFO);

        }

        void signalWatcher_Created(object sender, System.IO.FileSystemEventArgs e)
        {
            Logging.Write("Startup:Received a terminate request.", Logging.LogType.INFO);
            while (true)
            {
                try
                {
                    if (File.Exists(e.FullPath))
                        File.Delete(e.Name);
                    break;
                }
                catch (Exception ex)
                {
                    Logging.Write("Startup:terminate file delete error:" + ex.Message, Logging.LogType.WARNING);
                }
                Thread.Sleep(500);
            }
            SendTerminate();
        }

        void rubyProcess_OutputDataReceived(object sender, DataReceivedEventArgs outLine)
        {
            Logging.Write("OutputDataRec:" + outLine.Data, Logging.LogType.INFO);
            //Console.WriteLine("Redirect: {0}", outLine.Data);
        }

        //public void Read_StandardIO()
        //{
        //    var sr = (TextReader)rubyReader;
        //    if (rubyReader.EndOfStream)
        //    {
        //        Console.WriteLine("No Stream");
        //    }
        //    else
        //    {
        //        var d = sr.ReadToEnd();
        //        Console.WriteLine("hhhh:{0}", d);
        //    }
        //}


        public void SendTerminate()
        {
            Logging.Write("Startup:Generate a terminate request.", Logging.LogType.INFO);
            var ret = GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, 0);
            if (ret)
                Console.WriteLine("GenerateConsoleCtrlEvent true");
            else
                Console.WriteLine(Marshal.GetLastWin32Error().ToString());

        }



        
        [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool GenerateConsoleCtrlEvent(ConsoleCtrlEvent sigevent, int dwProcessGroupId); 
        public enum ConsoleCtrlEvent 
        { 
            CTRL_C = 0, 
            CTRL_BREAK = 1, 
            CTRL_CLOSE = 2, 
            CTRL_LOGOFF = 5, 
            CTRL_SHUTDOWN = 6 
        }

    }
}
